﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Microsoft.Xna.Framework;

namespace VoxelModel.Cameras
{
    /// <summary>
    /// this is essentially the only camera we have...
    /// </summary>
    public class ViewCamera
    {
        private Vector3 _position;
        private Vector3 _target;
        private Vector3 _upVector;

        private Matrix _viewMatrix;
        private Matrix _projectionMatrix;

        private float aspectRatio = 0.0f;

        private float _fieldOfView = (float)(70 * Math.PI / 180);
        private float _nearPlane = 0.01f;
        private float _farPlane = 1000; // random value...don't really know what to put here.

        private Vector3 _forward;
        private Vector3 _right;
        private Vector3 _up;

        private float rotationSensitivity = 0.001f;
        private float translationSensitivity = 1f;

        private Vector3 _cameraReference;

        private float _yaw;
        private float _pitch;

        public Vector3 Position
        {
            get { return _position; }
            set { _position = value; }
        }
        public Vector3 Target
        {
            get { return _target; }
            set { _target = value; }
        }

        public Matrix View
        {
            get { return _viewMatrix; }
            set { _viewMatrix = value; }
        }
        public Matrix Projection
        {
            get { return _projectionMatrix; }
            set { _projectionMatrix = value; }
        }

        public Vector3 Forward
        {
            get { return _forward; }
        }
        public Vector3 Right
        {
            get { return _right; }
        }
        public Vector3 Up
        {
            get { return _up; }
        }

        public Vector3 CameraReference
        {
            get { return _cameraReference; }
        }

        public float Yaw
        {
            get { return _yaw; }
        }

        public float Pitch
        {
            get { return _pitch; }
        }

        public ViewCamera(float aspectRatio, Vector3 position, Vector3 up, Vector3 target)
        {
            Position = position;
            _upVector = up;
            _cameraReference = target;

            this.aspectRatio = aspectRatio;
        }

        public void Initialize()
        {
            UpdateViewMatrix(Matrix.Identity);

            Projection = Matrix.CreatePerspectiveFieldOfView(_fieldOfView, aspectRatio, _nearPlane, _farPlane);
        }

        public void UpdateViewMatrix(Matrix rotation)
        {
            Vector3 rotatedUpVector = Vector3.Transform(Vector3.Up, rotation);
            View = Matrix.CreateLookAt(Position, Target, rotatedUpVector);

            _forward = Vector3.Normalize(Target - Position);
            _right = Vector3.Normalize(Vector3.Cross(_forward, rotatedUpVector));
            _up = Vector3.Normalize(Vector3.Cross(_right, _forward));
        }

        public void Update(Vector3 translation, float leftRight, float upDown)
        {
            _yaw += leftRight * rotationSensitivity;
            _pitch += upDown * rotationSensitivity;

            Matrix rotationMatrix = Matrix.CreateRotationX(this._pitch) * Matrix.CreateRotationY(this._yaw);
            Vector3 transformedReference = Vector3.Transform(_cameraReference, rotationMatrix);
            Position += Vector3.Transform(translation, Matrix.CreateRotationY(this._yaw)) * translationSensitivity;
            Target = transformedReference + Position;

            UpdateViewMatrix(rotationMatrix);
        }
    }
}
